package common

import (
	"context"
	"encoding/json"
	"errors"
	"github.com/filecoin-project/go-jsonrpc"
	"github.com/filecoin-project/lotus/api"
	"github.com/filecoin-project/lotus/api/client"
	"github.com/filecoin-project/lotus/chain/types"
	cliutil "github.com/filecoin-project/lotus/cli/util"
	"github.com/milkbobo/fishgoweb/language"
	"github.com/shopspring/decimal"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo/options"
	"golang.org/x/xerrors"
	"os"
	"strconv"
	"time"
)

type CommonFunc struct {
	BaseModel
}

var LocationTime = time.FixedZone("CST", 8*3600)

var LotusApi api.FullNode
var LotusMinerApi api.StorageMiner
var NowTipSet *types.TipSet
var LotusApiClose jsonrpc.ClientCloser
var LotusMinerApiClose jsonrpc.ClientCloser

func (this *CommonFunc) FilConvert(fil string) (float64, error) {
	filDecimal, err := decimal.NewFromString(fil)
	if err != nil {
		return 0, err
	}
	biFloat, _ := this.FilConvertByDecimal(filDecimal).Float64()
	return biFloat, nil
}

func (this *CommonFunc) FilConvertByDecimal(fil decimal.Decimal) decimal.Decimal {
	if fil.IsZero() {
		return fil
	}
	return fil.Div(decimal.NewFromInt(1e18))
}

func (this *CommonFunc) SizeTibConvert(kb string) (float64, error) {
	kbDecimal, err := decimal.NewFromString(kb)
	if err != nil {
		return 0, err
	}
	if kbDecimal.IsZero() {
		return 0, errors.New("参数不能为0")
	}
	TFloat, _ := this.SizeTibConvertByDecimal(kbDecimal).Float64()
	return TFloat, nil
}

func (this *CommonFunc) SizeTibConvertByDecimal(kbDecimal decimal.Decimal) decimal.Decimal {
	if kbDecimal.IsZero() {
		return kbDecimal
	}
	return kbDecimal.Div(decimal.NewFromInt(1024 * 1024 * 1024 * 1024))
}

func (this *CommonFunc) BsonMarshal(data interface{}) ([]byte, error) {
	return bson.Marshal(data)
}

func (this *CommonFunc) BsonUnmarshal(bsonByteData []byte) (bson.D, error) {
	result := bson.D{}
	err := bson.Unmarshal(bsonByteData, &result)

	return result, err
}

func (this *CommonFunc) StructToBsonD(data interface{}) (bson.D, error) {

	result := bson.D{}
	insertByte, err := bson.Marshal(data)
	if err != nil {
		return result, err
	}

	err = bson.Unmarshal(insertByte, &result)
	return result, err
}

func (this *CommonFunc) GetPaging() *options.FindOptions {
	current, err := strconv.ParseInt(this.Ctx.GetParam("current"), 10, 64)
	if err != nil {
		panic(err)
	}
	pageSize, err := strconv.ParseInt(this.Ctx.GetParam("pageSize"), 10, 64)
	if err != nil {
		panic(err)
	}
	pageIndex := (current - 1) * pageSize

	opts := options.Find()
	opts.Limit = &pageSize
	opts.Skip = &pageIndex
	return opts
}

func (this *CommonFunc) PostToStruct(data interface{}) {

	if this.Ctx.GetMethod() != "POST" {
		language.Throw(1, "请求Method不是POST方法: "+this.Ctx.GetMethod())
	}
	body, err := this.Ctx.GetBody()
	if err != nil {
		panic(err)
	}

	if len(body) == 0 {
		return
	}

	err = json.Unmarshal(body, &data)
	if err != nil {
		panic(err)
	}
}

func (this *CommonFunc) TimeToEpoch(epochTime string) (int64, error) {
	head, err := LotusApi.ChainHead(context.TODO())
	if err != nil {
		return 0, err
	}
	dateForMate := "2006-01-02 15:04:05"
	currentEpochTime := time.Now().Format(dateForMate)

	if epochTime == "" {
		epochTime = currentEpochTime
	}

	epochTimeParse, err := time.ParseInLocation(dateForMate, epochTime, LocationTime)
	if err != nil {
		return 0, err
	}
	currentEpochTimeParse, err := time.ParseInLocation(dateForMate, currentEpochTime, LocationTime)
	if err != nil {
		return 0, err
	}
	if epochTimeParse.Unix() > currentEpochTimeParse.Unix() {
		return 0, xerrors.Errorf("more than current time error")
	}

	currentEpoch := head.Height()

	//fmt.Printf("current epoch:%v\n", currentEpoch)
	//fmt.Printf("current time:%v\n", currentEpochTime)

	currentEpochTimeSecondsInt, err := strconv.Atoi(currentEpochTime[17:])
	if err != nil {
		return 0, err
	}
	currentEpochCreatedTime := ""
	if currentEpochTimeSecondsInt < 30 {
		currentEpochCreatedTime = currentEpochTime[:17] + "00"

	} else {
		currentEpochCreatedTime = currentEpochTime[:17] + "30"
	}
	currentEpochCreatedTimeParse, err := time.ParseInLocation(dateForMate, currentEpochCreatedTime, LocationTime)
	if err != nil {
		return 0, err
	}

	epochTimeSecondsInt, err := strconv.Atoi(epochTime[17:])
	if err != nil {
		return 0, err
	}
	epochCreatedTime := ""
	if epochTimeSecondsInt < 30 {
		epochCreatedTime = epochTime[:17] + "00"
	} else {
		epochCreatedTime = epochTime[:17] + "30"
	}
	epochCreatedTimeParse, err := time.ParseInLocation(dateForMate, epochCreatedTime, LocationTime)
	if err != nil {
		return 0, err
	}

	seconds := currentEpochCreatedTimeParse.Unix() - epochCreatedTimeParse.Unix()

	heightDifference := seconds / 30

	epochInt := int64(currentEpoch) - heightDifference
	return epochInt, nil
}


func LotusInit() {
	var err error

	// export FULLNODE_API_INFO=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJBbGxvdyI6WyJyZWFkIiwid3JpdGUiLCJzaWduIiwiYWRtaW4iXX0.lkJ3vYC00AeWqwwBuBHrYOqmUOZUCWrFd1UiKjRqZ_g:ws://113.107.201.187:1234/rpc/v0
	FULLNODE_API_INFO := os.Getenv("FULLNODE_API_INFO")
	if FULLNODE_API_INFO == "" {
		panic("FULLNODE_API_INFO系统环境变量不能为空")
	}

	fullnodeApiInfo := cliutil.ParseApiInfo(FULLNODE_API_INFO)

	LotusApi, LotusApiClose, err = client.NewFullNodeRPC(context.TODO(), fullnodeApiInfo.Addr, fullnodeApiInfo.AuthHeader())
	if err != nil {
		panic(err)
	}
}

func GetNowTipSet() {
	var err error
	NowTipSet, err = LotusApi.ChainHead(context.Background())
	if err != nil {
		//LotusApiClose()
		LotusInit()
		panic(err)
	}
}

func init() {
	LotusInit()
	GetNowTipSet()
}
